import socket
import subprocess
import re
import platform
import logging
logger = logging.getLogger()


class SocketOperation():
    def __init__(self):
        if platform.system().lower() == 'windows':
            self.__platform = "windows"
        elif platform.system().lower() == 'linux':
            self.__platform = "linux"
        else:
            self.__platform = None

    def __ping_ip(self, ip):
        sp = subprocess.Popen(["ping.exe ", ip], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
                              stderr=subprocess.PIPE, shell=True)
        out_encode = sp.stdout.read()
        try:
            out = out_encode.decode()
        except Exception:
            out = out_encode.decode(encoding="gbk")
        regex = re.compile("([\s\S]*?) = (\d+)ms[, |，]([\s\S]*?) = (\d+)ms[, |，]([\s\S]*?) = (\d+)ms", re.IGNORECASE)
        find_result = regex.findall(out)
        if find_result:
            return True
        else:
            return False

    def check_connected(self, ip):
        if self.__platform == "windows":
            return self.__ping_ip(ip)
        elif self.__platform == "linux":
            p = subprocess.Popen(["ping -c 1 -w 1 {}".format(ip)], stdout=subprocess.PIPE, stderr=subprocess.PIPE,
                                 shell=True)
            out = p.stdout.read().decode()
            regex = re.compile('100% packet loss')
            find_result = regex.findall(out)
            if find_result:
                return False
            else:
                return True
        else:
            return self.__ping_ip(ip)

    def check_address_port(self, ip_list, port_list):
        if type(ip_list) not in [list, str]:
            raise Exception("port_list must be a str or list!")
        if type(port_list) not in [list, str]:
            raise Exception("port_list must be a str or list!")
        if type(ip_list) == str:
            ip_list = [ip_list]
        if type(port_list) == str:
            port_list = [port_list]
        error_flag = False
        port_scan_result = dict()
        for ip in ip_list:
            connected_code = self.check_connected(ip)
            if not connected_code:
                port_scan_result[ip] = 0
                error_flag = True
            else:
                port_scan_result_child = dict()
                for port in port_list:
                    try:
                        port = int(port)
                    except Exception as e:
                        logger.error(e)
                        raise Exception("port: an integer is required！")
                    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                    result = sock.connect_ex((ip, port))
                    sock.close()
                    port_scan_result_child[port] = 1 if result == 0 else 2
                    if result != 0:
                        error_flag = True
                port_scan_result[ip] = port_scan_result_child
        if error_flag:
            logger.info(port_scan_result)
            raise Exception("The IP is not connected or the port is not opened, please check!")
        return port_scan_result
